﻿///Code based on
/// http://chessbin.com/page/Chess-Game-Starer-Kit.aspx

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Core.Models.Enums;

namespace Core.Models
{
    internal static class PieceValidMoves
    {
        internal static bool[] BlackAttackBoard;
        internal static bool[] WhiteAttackBoard;


        /// <summary>
        /// Performs a deep analysis of the board and records checks for the special scenarios (en passant, checks, kills,etc.)
        /// </summary>
        /// <param name="board"></param>
        /// <param name="dstPos"></param>
        /// <param name="pcMoving"></param>
        /// <returns></returns>
        private static bool AnalyzeMove(Board board, byte dstPos, Piece pcMoving, byte srcPos)
        {
            //If I am not a pawn everywhere I move I can attack
            if (pcMoving.PieceColor == ChessPieceColor.White)
            {
                WhiteAttackBoard[dstPos] = true;
            }
            else
            {
                BlackAttackBoard[dstPos] = true;
            }
            //If there no piece there I can potentialy kill just add the move and exit
            if (board.Squares[dstPos].Piece == null)
            {
                pcMoving.ValidMoves.Push(dstPos);

                return true;
            }

            Piece pcAttacked = board.Squares[dstPos].Piece;


            //if that piece is a different color
            if (pcAttacked.PieceColor != pcMoving.PieceColor)
            {
                pcAttacked.IsAttackedBy += pcMoving.PieceValue;
                pcAttacked.nbIsAttackedBy ++;
                pcMoving.IsAttacking += pcAttacked.PieceValue;
                //If this is a king set it in check                   
                if (pcAttacked.PieceType == ChessPieceType.King)
                {
                    if (pcAttacked.PieceColor == ChessPieceColor.Black)
                    {
                        board.PiecesAttackingBlackKing[board.AmountPieceAttackingBlackKing] = srcPos;
                        board.AmountPieceAttackingBlackKing++;
                        board.BlackCheck = true;
                    }
                    else
                    {
                        board.PiecesAttackingWhiteKing[board.AmountPieceAttackingWhiteKing] = srcPos;
                        board.AmountPieceAttackingWhiteKing++;
                        board.WhiteCheck = true;
                    }
                }
                else
                {
                    //Add this as a valid move
                    pcMoving.ValidMoves.Push(dstPos);
                }
                //We don't continue movement past this piece

                //if (dstPos == 47 && pcMoving.PieceType == ChessPieceType.Knight)
                //{
                //    Debug.WriteLine("");
                //}

                return false;
            }
            //Same Color I am defending
            pcAttacked.IsDefendedBy += pcMoving.PieceValue;
            pcAttacked.nbIsDefendedBy ++;
            pcMoving.IsDefending += pcAttacked.PieceValue;

            //Since this piece is of my kind I can't move there
            //if (dstPos == 47 && pcMoving.PieceType == ChessPieceType.Knight)
            //{
            //    Debug.WriteLine("");
            //}
            return false;
        }

        /// <summary>
        /// Since pawns are special and they don't kill eveything they touch, a special method is necessary
        /// This one loops through all the possible pawn moves
        /// </summary>
        /// <param name="moves"></param>
        /// <param name="pcMoving"></param>
        /// <param name="srcPosition"></param>
        /// <param name="board"></param>
        /// <param name="count"></param>
        private static void CheckValidMovesPawn(List<byte> moves, Piece pcMoving,byte srcPosition,Board board, byte count)
        {
            for (byte i = 0; i < count; i++)
            {
                byte dstPos = moves[i];
                if (dstPos % 8 != srcPosition % 8)
                {
                    //If there is a piece there I can potentialy kill
                    AnalyzeMovePawn(board, dstPos, pcMoving, srcPosition);

                    if (pcMoving.PieceColor == ChessPieceColor.White)
                    {
                        WhiteAttackBoard[dstPos] = true;
                    }
                    else
                    {
                        BlackAttackBoard[dstPos] = true;
                    }
                }
                // if there is something if front pawns can't move there
                else if (board.Squares[dstPos].Piece != null)
                {
                    return;
                }
                //if there is nothing in front of me (blocked == false)
                else
                {
                    pcMoving.ValidMoves.Push(dstPos);
                }
            }
        }

        /// <summary>
        /// This methods checks if the moves are legal and the special cases (en passant, killing, etc.)
        /// </summary>
        /// <param name="board"></param>
        /// <param name="dstPos"></param>
        /// <param name="pcMoving"></param>
        private static void AnalyzeMovePawn(Board board, byte dstPos, Piece pcMoving, byte srcPos)
        {
            //Because Pawns only kill diagonaly we handle the En Passant scenario specialy
            if (board.EnPassantPosition > 0)
            {
                if (pcMoving.PieceColor != board.EnPassantColor)
                {
                    if (board.EnPassantPosition == dstPos)
                    {
                        //We have an En Passant Possible
                        pcMoving.ValidMoves.Push(dstPos);
                        if (pcMoving.PieceColor == ChessPieceColor.White)
                        {
                            WhiteAttackBoard[dstPos] = true;
                        }
                        else
                        {
                            BlackAttackBoard[dstPos] = true;
                        }
                    }
                }
            }

            Piece pcAttacked = board.Squares[dstPos].Piece;

            //If there no piece there I can potentialy kill
            if (pcAttacked == null)
                return;

            //Regardless of what is there I am attacking this square
            if (pcMoving.PieceColor == ChessPieceColor.White)
            {
                WhiteAttackBoard[dstPos] = true;

                //if that piece is the same color
                if (pcAttacked.PieceColor == pcMoving.PieceColor)
                {
                    pcAttacked.IsDefendedBy += pcMoving.PieceValue;
                    pcAttacked.nbIsDefendedBy++;
                    pcMoving.IsDefending += pcAttacked.PieceValue;
                    return;
                }
                //else piece is different so we are attacking
                pcAttacked.IsAttackedBy += pcMoving.PieceValue;
                pcAttacked.nbIsAttackedBy++;
                pcMoving.IsAttacking += pcAttacked.PieceValue;

                //If this is a king set it in check                   
                if (pcAttacked.PieceType == ChessPieceType.King)
                {
                    board.PiecesAttackingBlackKing[board.AmountPieceAttackingBlackKing] = srcPos;
                    board.AmountPieceAttackingBlackKing++;
                    board.BlackCheck = true;
                }
                else
                {
                    //Add this as a valid move
                    pcMoving.ValidMoves.Push(dstPos);
                }
            }
            else
            {
                BlackAttackBoard[dstPos] = true;

                //if that piece is the same color
                if (pcAttacked.PieceColor == pcMoving.PieceColor)
                {
                    return;
                }

                //If this is a king set it in check                   
                if (pcAttacked.PieceType == ChessPieceType.King)
                {
                    board.PiecesAttackingWhiteKing[board.AmountPieceAttackingWhiteKing] = srcPos;
                    board.AmountPieceAttackingWhiteKing++;
                    board.WhiteCheck = true;
                }
                else
                {
                    //Add this as a valid move
                    pcMoving.ValidMoves.Push(dstPos);
                }
            }
            return;
        }

        /// <summary>
        /// Check if castling is legal
        /// </summary>
        /// <param name="board"></param>
        /// <param name="king"></param>
        private static void GenerateValidMovesKingCastle(Board board, Piece king)
        {
            if (king == null)
            {
                return;
            }
            if (king.Moved)
            {
                return;
            }
            if (king.PieceColor == ChessPieceColor.White &&
             board.WhiteCastled)
            {
                return;
            }
            if (king.PieceColor == ChessPieceColor.Black &&
             board.BlackCastled)
            {
                return;
            }
            if (king.PieceColor == ChessPieceColor.Black &&
             board.BlackCheck)
            {
                return;
            }
            if (king.PieceColor == ChessPieceColor.White &&
             board.WhiteCheck)
            {
                return;
            }


            //This code will add the castleling move to the pieces available moves
            if (king.PieceColor == ChessPieceColor.White)
            {
                if (board.WhiteCheck)
                {
                    return;
                }

                if (board.Squares[63].Piece != null)
                {
                    //Check if the Right Rook is still in the correct position
                    if (board.Squares[63].Piece.PieceType == ChessPieceType.Rook)
                    {
                        if (board.Squares[63].Piece.PieceColor == king.PieceColor)
                        {
                            //Move one column to right see if its empty
                            if (board.Squares[62].Piece == null)
                            {
                                if (board.Squares[61].Piece == null)
                                {
                                    if (BlackAttackBoard[61] == false &&
                                     BlackAttackBoard[62] == false)
                                    {
                                        //Ok looks like move is valid lets add it
                                        king.ValidMoves.Push(62);
                                        WhiteAttackBoard[62] = true;
                                    }
                                }
                            }
                        }
                    }
                }

                if (board.Squares[56].Piece != null)
                {
                    //Check if the Left Rook is still in the correct position
                    if (board.Squares[56].Piece.PieceType == ChessPieceType.Rook)
                    {
                        if (board.Squares[56].Piece.PieceColor == king.PieceColor)
                        {
                            //Move one column to right see if its empty
                            if (board.Squares[57].Piece == null)
                            {
                                if (board.Squares[58].Piece == null)
                                {
                                    if (board.Squares[59].Piece == null)
                                    {
                                        if (BlackAttackBoard[58] == false &&
                                         BlackAttackBoard[59] == false)
                                        {
                                            //Ok looks like move is valid lets add it
                                            king.ValidMoves.Push(58);
                                            WhiteAttackBoard[58] = true;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else if (king.PieceColor == ChessPieceColor.Black)
            {
                if (board.BlackCheck)
                {
                    return;
                }

                //There are two ways to castle, scenario 1:
                if (board.Squares[7].Piece != null)
                {
                    //Check if the Right Rook is still in the correct position
                    if (board.Squares[7].Piece.PieceType == ChessPieceType.Rook
                     && !board.Squares[7].Piece.Moved)
                    {
                        if (board.Squares[7].Piece.PieceColor == king.PieceColor)
                        {
                            //Move one column to right see if its empty

                            if (board.Squares[6].Piece == null)
                            {
                                if (board.Squares[5].Piece == null)
                                {
                                    if (WhiteAttackBoard[5] == false && WhiteAttackBoard[6] == false)
                                    {
                                        //Ok looks like move is valid lets add it
                                        king.ValidMoves.Push(6);
                                        BlackAttackBoard[6] = true;
                                    }
                                }
                            }
                        }
                    }
                }
                //There are two ways to castle, scenario 2:
                if (board.Squares[0].Piece != null)
                {
                    //Check if the Left Rook is still in the correct position
                    if (board.Squares[0].Piece.PieceType == ChessPieceType.Rook &&
                     !board.Squares[0].Piece.Moved)
                    {
                        if (board.Squares[0].Piece.PieceColor ==
                         king.PieceColor)
                        {
                            //Move one column to right see if its empty
                            if (board.Squares[1].Piece == null)
                            {
                                if (board.Squares[2].Piece == null)
                                {
                                    if (board.Squares[3].Piece == null)
                                    {
                                        if (WhiteAttackBoard[2] == false &&
                                         WhiteAttackBoard[3] == false)
                                        {
                                            //Ok looks like move is valid lets add it
                                            king.ValidMoves.Push(2);
                                            BlackAttackBoard[2] = true;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        private static void GenerateValidMovesKing(Piece piece, Board board, byte srcPosition)
        {
            if (piece == null)
            {
                return;
            }

            for (byte i = 0; i < PieceMoves.MoveArrays.KingTotalMoves[srcPosition]; i++)
            {
                byte dstPos = PieceMoves.MoveArrays.KingMoves[srcPosition].Moves[i];

                if (piece.PieceColor == ChessPieceColor.White)
                {
                    //I can't move where I am being attacked
                    if (BlackAttackBoard[dstPos])
                    {
                        WhiteAttackBoard[dstPos] = true;
                        continue;
                    }
                }
                else
                {
                    if (WhiteAttackBoard[dstPos])
                    {
                        BlackAttackBoard[dstPos] = true;
                        continue;
                    }
                }

                AnalyzeMove(board, dstPos, piece, srcPosition);
            }
        }

        internal static void GenerateValidMoves(Board board)
        {
            // Reset Board
            board.BlackCheck = false;
            board.WhiteCheck = false;
            WhiteAttackBoard = new bool[64];
            BlackAttackBoard = new bool[64];

            //Generate Moves
            for (byte x = 0; x < 64; x++)
            {
                Square sqr = board.Squares[x];

                if (sqr.Piece == null)
                    continue;

                sqr.Piece.IsDefendedBy = 0;
                sqr.Piece.IsDefending = 0;
                sqr.Piece.IsAttackedBy = 0;
                sqr.Piece.IsAttacking = 0;
                sqr.Piece.nbIsAttackedBy = 0;
                sqr.Piece.nbIsDefendedBy = 0;

                sqr.Piece.ValidMoves = new Stack<byte>(sqr.Piece.LastValidMoveCount);

                switch (sqr.Piece.PieceType)
                {
                    case ChessPieceType.Pawn:
                        {
                            if (sqr.Piece.PieceColor == ChessPieceColor.White)
                            {
                                CheckValidMovesPawn(PieceMoves.MoveArrays.WhitePawnMoves[x].Moves, sqr.Piece, x,
                                     board,
                                     PieceMoves.MoveArrays.WhitePawnTotalMoves[x]);
                                break;
                            }
                            if (sqr.Piece.PieceColor == ChessPieceColor.Black)
                            {
                                CheckValidMovesPawn(PieceMoves.MoveArrays.BlackPawnMoves[x].Moves, sqr.Piece, x,
                                     board,
                                     PieceMoves.MoveArrays.BlackPawnTotalMoves[x]);
                                break;
                            }

                            break;
                        }
                    case ChessPieceType.Knight:
                        {
                            for (byte i = 0; i < PieceMoves.MoveArrays.KnightTotalMoves[x]; i++)
                            {
                                AnalyzeMove(board, PieceMoves.MoveArrays.KnightMoves[x].Moves[i], sqr.Piece, x);
                            }

                            break;
                        }
                    case ChessPieceType.Bishop:
                        {
                            for (byte i = 0; i < PieceMoves.MoveArrays.BishopTotalMoves1[x]; i++)
                            {
                                if (
                                 AnalyzeMove(board, PieceMoves.MoveArrays.BishopMoves1[x].Moves[i],
                                    sqr.Piece, x) ==
                                 false)
                                {
                                    break;
                                }
                            }
                            for (byte i = 0; i < PieceMoves.MoveArrays.BishopTotalMoves2[x]; i++)
                            {
                                if (
                                 AnalyzeMove(board, PieceMoves.MoveArrays.BishopMoves2[x].Moves[i],
                                    sqr.Piece, x) ==
                                 false)
                                {
                                    break;
                                }
                            }
                            for (byte i = 0; i < PieceMoves.MoveArrays.BishopTotalMoves3[x]; i++)
                            {
                                if (
                                 AnalyzeMove(board, PieceMoves.MoveArrays.BishopMoves3[x].Moves[i],
                                    sqr.Piece, x) ==
                                 false)
                                {
                                    break;
                                }
                            }
                            for (byte i = 0; i < PieceMoves.MoveArrays.BishopTotalMoves4[x]; i++)
                            {
                                if (
                                 AnalyzeMove(board, PieceMoves.MoveArrays.BishopMoves4[x].Moves[i],
                                    sqr.Piece, x) ==
                                 false)
                                {
                                    break;
                                }
                            }

                            break;
                        }
                    case ChessPieceType.Rook:
                        {
                            for (byte i = 0; i < PieceMoves.MoveArrays.RookTotalMoves1[x]; i++)
                            {
                                if (
                                 AnalyzeMove(board, PieceMoves.MoveArrays.RookMoves1[x].Moves[i], sqr.Piece, x) ==
                                 false)
                                {
                                    break;
                                }
                            }
                            for (byte i = 0; i < PieceMoves.MoveArrays.RookTotalMoves2[x]; i++)
                            {
                                if (
                                 AnalyzeMove(board, PieceMoves.MoveArrays.RookMoves2[x].Moves[i], sqr.Piece, x) ==
                                 false)
                                {
                                    break;
                                }
                            }
                            for (byte i = 0; i < PieceMoves.MoveArrays.RookTotalMoves3[x]; i++)
                            {
                                if (
                                 AnalyzeMove(board, PieceMoves.MoveArrays.RookMoves3[x].Moves[i], sqr.Piece, x) ==
                                 false)
                                {
                                    break;
                                }
                            }
                            for (byte i = 0; i < PieceMoves.MoveArrays.RookTotalMoves4[x]; i++)
                            {
                                if (
                                 AnalyzeMove(board, PieceMoves.MoveArrays.RookMoves4[x].Moves[i], sqr.Piece, x) ==
                                 false)
                                {
                                    break;
                                }
                            }

                            break;
                        }
                    case ChessPieceType.Queen:
                        {
                            for (byte i = 0; i < PieceMoves.MoveArrays.QueenTotalMoves1[x]; i++)
                            {
                                if (
                                 AnalyzeMove(board, PieceMoves.MoveArrays.QueenMoves1[x].Moves[i], sqr.Piece, x) ==
                                 false)
                                {
                                    break;
                                }
                            }
                            for (byte i = 0; i < PieceMoves.MoveArrays.QueenTotalMoves2[x]; i++)
                            {
                                if (
                                 AnalyzeMove(board, PieceMoves.MoveArrays.QueenMoves2[x].Moves[i], sqr.Piece, x) ==
                                 false)
                                {
                                    break;
                                }
                            }
                            for (byte i = 0; i < PieceMoves.MoveArrays.QueenTotalMoves3[x]; i++)
                            {
                                if (
                                 AnalyzeMove(board, PieceMoves.MoveArrays.QueenMoves3[x].Moves[i], sqr.Piece, x) ==
                                 false)
                                {
                                    break;
                                }
                            }
                            for (byte i = 0; i < PieceMoves.MoveArrays.QueenTotalMoves4[x]; i++)
                            {
                                if (
                                 AnalyzeMove(board, PieceMoves.MoveArrays.QueenMoves4[x].Moves[i], sqr.Piece, x) ==
                                 false)
                                {
                                    break;
                                }
                            }

                            for (byte i = 0; i < PieceMoves.MoveArrays.QueenTotalMoves5[x]; i++)
                            {
                                if (
                                 AnalyzeMove(board, PieceMoves.MoveArrays.QueenMoves5[x].Moves[i], sqr.Piece, x) ==
                                 false)
                                {
                                    break;
                                }
                            }
                            for (byte i = 0; i < PieceMoves.MoveArrays.QueenTotalMoves6[x]; i++)
                            {
                                if (
                                 AnalyzeMove(board, PieceMoves.MoveArrays.QueenMoves6[x].Moves[i], sqr.Piece, x) ==
                                 false)
                                {
                                    break;
                                }
                            }
                            for (byte i = 0; i < PieceMoves.MoveArrays.QueenTotalMoves7[x]; i++)
                            {
                                if (
                                 AnalyzeMove(board, PieceMoves.MoveArrays.QueenMoves7[x].Moves[i], sqr.Piece, x) ==
                                 false)
                                {
                                    break;
                                }
                            }
                            for (byte i = 0; i < PieceMoves.MoveArrays.QueenTotalMoves8[x]; i++)
                            {
                                if (
                                 AnalyzeMove(board, PieceMoves.MoveArrays.QueenMoves8[x].Moves[i], sqr.Piece, x) ==
                                 false)
                                {
                                    break;
                                }
                            }

                            break;
                        }
                    case ChessPieceType.King:
                        {
                            if (sqr.Piece.PieceColor == ChessPieceColor.White)
                            {
                                board.WhiteKingPosition = x;
                            }
                            else
                            {
                                board.BlackKingPosition = x;
                            }

                            break;
                        }
                }
            }

            if (board.WhoseMove == ChessPieceColor.White)
            {
                GenerateValidMovesKing(board.Squares[board.BlackKingPosition].Piece, board,
                        board.BlackKingPosition);
                GenerateValidMovesKing(board.Squares[board.WhiteKingPosition].Piece, board,
                        board.WhiteKingPosition);
            }
            else
            {
                GenerateValidMovesKing(board.Squares[board.WhiteKingPosition].Piece, board,
                        board.WhiteKingPosition);
                GenerateValidMovesKing(board.Squares[board.BlackKingPosition].Piece, board,
                        board.BlackKingPosition);
            }

            //Now that all the pieces were examined we know if the king is in check
            GenerateValidMovesKingCastle(board, board.Squares[board.WhiteKingPosition].Piece);
            GenerateValidMovesKingCastle(board, board.Squares[board.BlackKingPosition].Piece);
        }

        
    }
}
